#!/usr/bin/env python3
#

import os
import syslog
import threading
from swsscommon.swsscommon import ConfigDBConnector
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# FILE
RADIUS_PAM_AUTH_CONF_DIR = "/etc/pam_radius_auth.d/"
RADIUS_PAM_AUTH_CONF_STATS_DIR = "/etc/pam_radius_auth.d/statistics/"

class RadiusCountersDbMon (threading.Thread):
    def __init__(self, ID, name, radiusStatsInstance):
        threading.Thread.__init__(self)
        self.ID = ID
        self.name = name
        self.radiusStatsInstance = radiusStatsInstance

    def handle_CountersDbRadiusClear(self, key, data):
        # print("RadiusCountersDbMon.handle_CountersDbRadiusClear()")
        if key == 'clear':
            self.radiusStatsInstance.handle_clear()

    def run(self):
        # print("RadiusCountersDbMon.run()")
        self.radiusStatsInstance.counters_db.subscribe('RADIUS', lambda table, key, data: self.handle_CountersDbRadiusClear(key, data))
        self.radiusStatsInstance.counters_db.listen()
        # print("RadiusCountersDbMon.run(): After listen()")


class RadiusStatsFileHandler(FileSystemEventHandler):
    def __init__(self, radiusStatsInstance):
        self.radiusStatsInstance = radiusStatsInstance

    def on_any_event(self, event):
        # print("RadiusStatsFileHandler.on_any_event()")
        if event.is_directory:
            return None

        self.radiusStatsInstance.handle_update(os.path.basename(event.src_path))

class RadiusStatsFileMon ():
    def __init__(self, radiusStatsInstance):
        self.event_handler = RadiusStatsFileHandler(radiusStatsInstance)
        self.observer = Observer()
        self.observer.schedule(self.event_handler, RADIUS_PAM_AUTH_CONF_STATS_DIR, recursive=False)
        self.observer.start()
        # print("RadiusStatsFileMon.__init__(): After observer.start()")

    def stop(self):
        # print("RadiusStatsFileMon.stop()")
        self.observer.stop()
        # print("RadiusStatsFileMon.stop(): After observer.stop()")
        self.observer.join()
        # print("RadiusStatsFileMon.stop(): After observer.join()")


class RadiusStatistics:
    def __init__(self, cfg_db, rad_global_conf, radius_conf):

        self.radius_counter_names = [
            "counter_0",
            "access_requests",
            "access_accepts",
            "access_rejects",
            "accounting_requests",
            "accounting_responses",
            "counter_6",
            "counter_7",
            "counter_8",
            "counter_9",
            "counter_10",
            "access_challenges",
            "counter_12",
            "counter_13",
            "counter_14",
            "counter_15",
            "counter_16",
            "retried_access_requests",
            "counter_18",
            "counter_19",
            "retried_accounting_requests",
            "counter_21",
            "counter_22",
            "counter_23",
            "counter_24",
            "counter_25",
            "counter_26",
            "retried_access_challenges",
            "counter_28",
            "counter_29",
            "counter_30",
            "counter_31",
            "timeouts",
            "bad_authenticators",
            "invalid_packets",
            "counter_35",
        ]

        self.radius_global = {
            'statistics': 'False'
        }

        self.radius_servers = {}

        self.config_db = cfg_db

        for row in rad_global_conf:
            self.radius_global_update(row, rad_global_conf[row])

        for row in radius_conf:
            self.radius_server_update(row, radius_conf[row])


        self.counters_db = ConfigDBConnector()
        self.counters_db.db_connect('COUNTERS_DB', wait_for_init=False,
		retry_on=True)
        syslog.syslog(syslog.LOG_INFO, 'CountersDB connect success')
        self.dbmon_thread = RadiusCountersDbMon("RadiusCountersDbMon",
                                "RadiusCountersDbMon", self)
        self.dbmon_thread.daemon = True
        self.dbmon_thread.start()

        self.filemon = RadiusStatsFileMon(self)
        syslog.syslog(syslog.LOG_INFO, 'RADIUS Stats File Monitor started')

    def radius_global_update(self, key, data):
        if key == 'global':
            self.radius_global.update(data)

        for addr in self.radius_servers:
            self.create_file(addr)

    def radius_server_update(self, key, data):
        if data == {}:
            if key in self.radius_servers:
                del self.radius_servers[key]
        else:
            self.radius_servers[key] = data

        self.create_file(key)

    def create_file(self, addr):
        # print( "RadiusStatistics.create_file({})".format(addr))
        stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + addr
        if self.radius_global['statistics'] == 'False':
            if os.path.exists(stats_file):
                os.unlink(stats_file)
        else:
            open(stats_file, 'a').close()
            os.chmod(stats_file, 0o644)
            self.handle_update(addr)

    def handle_clear(self):
        # print( "RadiusStatistics.handle_clear()")
        for filename in os.listdir(RADIUS_PAM_AUTH_CONF_STATS_DIR):
            stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + filename
            open(stats_file, 'w').close()

    def handle_update(self, srv):
        # print( "RadiusStatistics.handle_update({})".format(srv))
        if self.radius_global['statistics'] == 'False':
            return

        stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + srv
        entry = None
        if os.path.exists(stats_file):
            with open(stats_file, 'r') as f:
                lines = f.readlines()
            if len(lines) > 0:
                radius_counters = lines[0].split(' ')
                entry = dict(zip(self.radius_counter_names, radius_counters))

        counters_db = ConfigDBConnector()
        counters_db.db_connect('COUNTERS_DB', wait_for_init=False,
		retry_on=False)

        counters_db.set_entry('RADIUS_SERVER_STATS', srv, entry)

        counters_db.close(counters_db.COUNTERS_DB)

class AAAStatsDaemon:
    def __init__(self):
        self.config_db = ConfigDBConnector()
        self.config_db.connect(wait_for_init=True, retry_on=True)
        syslog.syslog(syslog.LOG_INFO, 'ConfigDB connect success')

        radius_global = self.config_db.get_table('RADIUS')
        radius_server = self.config_db.get_table('RADIUS_SERVER')

        self.radiusstats = RadiusStatistics(self.config_db, radius_global,
				radius_server)

    def radius_global_handler(self, key, data):
        self.radiusstats.radius_global_update(key, data)

    def radius_server_handler(self, key, data):
        self.radiusstats.radius_server_update(key, data)

    def start(self):
        self.config_db.subscribe('RADIUS_SERVER',
		lambda table, key, data: self.radius_server_handler(key, data))
        self.config_db.subscribe('RADIUS',
		lambda table, key, data: self.radius_global_handler(key, data))
        self.config_db.listen()
        # print( "After config_db.listen()")
        syslog.syslog(syslog.LOG_INFO, 'Stopping FileMon')
        self.radiusstats.filemon.stop()
        # print( "Exiting")
        syslog.syslog(syslog.LOG_INFO, 'Exiting')

def main():
    daemon = AAAStatsDaemon()
    daemon.start()


if __name__ == "__main__":
    main()

